CON

  'IO pins
  SDA           = 29            '
  SCL           = 28            '
  MUX           = 27            '
  CLOCK         = 26            '
  AUX5          = 25            ' TRIGGER
  AUX4          = 24            ' COMPARE
  AUX3          = 23            '           DAC_MSB     LSA_MSB 
  AUX2          = 22            '           DAC
  AUX1          = 21            '           DAC
  AUX0          = 20            '           DAC         LSA_LSB
  D19           = 19            '           DAC
  D18           = 18            '           DAC
  D17           = 17            '           DAC
  D16           = 16            '           DAC_LSB
  D15           = 15            '                       TV_AUR    
  D14           = 14            '                       TV_MSB    
  D13           = 13            '                       TV
  D12           = 12            '                       TV_LSB    
  D11           = 11            '           RANGE     
  D10           = 10            '           DAC_ENABLE

  'IO pin aliases
  TRIGGER       = AUX5
  COMPARE       = AUX4
  DAC_MSB       = AUX3
  LSA_MSB       = AUX3
  LSA_LSB       = AUX0
  DAC_LSB       = D16
  TV_AUR        = D15
  TV_MSB        = D14
  TV_LSB        = D12
  RANGE         = D11
  DAC_ENABLE    = D10

  'Voltages
  TRIGGER_ZERO  = $7F_FF_FF_FF
  TRIGGER_VOLT  = $0C_CC_CC_CC'$FF_FF_FF_FF / 20

  DAC_BI_ZERO   = $7F
  DAC_BI_VOLT   = $FF / 3

  DAC_PO_ZERO   = $00
  DAC_PO_VOLT   = $FF / 5

  'IO expander modes
  LSA_ADC         = 0           'Enables LSA; also allows 2nd channel of oscilloscope to operate
  LSA_TV          = 1           'Enables LSA and TV
  DAC_BIPOLAR_TV  = 2
  DAC_POSITIVE_TV = 3

{{ 
              CARD  P10  P11  DAC      LSA  TV   Trigger
  LSA_ADC     0     x    x    Off      On   Off  On
  LSA_TV      1     0    x    Off      On   On   On
  DAC_BI_TV   1     1    0    Bipolar  Off  On   On
  DAC_POS_TV  1     1    1    Positive Off  On   On
}}
  
  'IO expander pins             ' Function:             High:                   Low (default)
  IO_READ       = 0
  IO_WRITE      = 1
  IO_TOGGLE     = 2
  IO_DIRECTION  = 3

  CARD          = %1000_0000    ' Disable 2nd ADC chan  Disabled                Enabled
  PWR_ENABLE    = %0100_0000
  CHB_RANGE     = %0010_0000    ' Range on ADC          1 volt peak-to-peak     2 volts peak-to-peak
  CHA_RANGE     = %0001_0000
  CHB_DIV       = %0000_1000    ' Input divider         Divide by 20            Divide by 2
  CHA_DIV       = %0000_0100
  CHB_DC        = %0000_0010    ' AC/DC coupling        AC coupled              DC coupled
  CHA_DC        = %0000_0001

  'I2C constants
  ACK           = 0                              ' Acknowledge bit = 0
  NACK          = 1                              ' (No) acknowledge bit = 1
  WRITE_FLAG    = 0
  READ_FLAG     = 1
  EEPROM_ADR    = $50
  IO_EXP_ADR    = $70
  IO_EXP_READ   = IO_EXP_ADR << 1 | READ_FLAG
  IO_EXP_WRITE  = IO_EXP_ADR << 1 | WRITE_FLAG


VAR

  Byte  PinState[3]
  Byte  OutState[3]


PUB Initialize ' Turn analog front end on and all relays off

  InitializeI2C
  High(PWR_ENABLE)
  'High(CHA_RANGE | CHB_RANGE)
  
  outa[CARD]~
  dira[CARD]~~
  outa[MUX]~~
  dira[MUX]~~

  
PUB Set(PinMask) | Port ' Set all IO expander pins in PinMask high

  PinMask &= $FF

  Put(IO_WRITE, IO_EXP_WRITE, PinState[Port] |= PinMask)

  
PUB Clear(PinMask) | Port ' Set all IO expander pins in PinMask low

  PinMask &= $FF

  Put(IO_WRITE, IO_EXP_WRITE, PinState[Port] &= !PinMask)

  
PUB High(PinMask) | Port ' Set all IO expander pins in PinMask high and set as outputs

  PinMask &= $FF
  
  Put(IO_WRITE, IO_EXP_WRITE, PinState[Port] |= PinMask)
  Out(PinMask)

  
PUB Low(PinMask) | Port ' Set all IO expander pins in PinMask low

  PinMask &= $FF

  Put(IO_WRITE, IO_EXP_WRITE, PinState[Port] &= !PinMask)
  Out(PinMask)

  
PUB Toggle(PinMask) | Port ' Toggle state of all IO expander pins in PinMask

  Out(PinMask)

  PinMask &= $FF
  
  Put(IO_WRITE, IO_EXP_WRITE, PinState[Port] ^= PinMask)

{  
PUB Dir(PinMask) | Port ' Set all IO expander pins in PinMask as outputs

  PinMask &= $FF
  
  Put(IO_DIRECTION, IO_EXP_WRITE, OutState[Port] &= !PinMask)

}  
PUB In(PinMask) | Port ' Set all IO expander pins in PinMask as inputs

  PinMask &= $FF
  
  Put(IO_DIRECTION, IO_EXP_WRITE, OutState[Port] |= PinMask)

  
PUB Out(PinMask) | Port ' Set all IO expander pins in PinMask as outputs

  PinMask &= $FF
  
  Put(IO_DIRECTION, IO_EXP_WRITE, OutState[Port] &= !PinMask)

  
PUB Switch(PinMask) | Port ' Toggle direction of all IO expander pins in PinMask

  PinMask &= $FF
  
  Put(IO_DIRECTION, IO_EXP_WRITE, OutState[Port] ^= PinMask)


PUB Read(PinMask)

  return Get(PinMask >> 8, 3) & PinMask


PRI Put(Command, Address, Data)
{
  case Port & %11
    1:
      Port := $E0
    2: 
      Port := $42
      Address <<= 1
    3: 
      Port := $42
      Address := Address << 1 + 1
}      
  StartI2C
  WriteI2C(Address)         
  WriteI2C(Command)
  WriteI2C(Data)
  StopI2C


PRI Get(Port, Address)

  case Port
    1:
      Port := $E1
    2: 
      Port := $43
      Address <<= 1
    3: 
      Port := $43
      Address := Address << 1 + 1

  StartI2C
  WriteI2C($43)
  result := ReadI2C(3) & $FF                
  StopI2C


PRI InitializeI2C               ' An I2C device may be left in an

  

  outa[SCL]~                    ' Set clock low                                 
  dira[SCL]~~                   '  and as an output
  dira[SDA]~                    ' Set SDA as input (let it float high)
  repeat 10                      
    outa[SCL]~                  ' Put out up to 9 to 10 clock pulses
    outa[SCL]~~


PRI StartI2C                    ' SDA goes high to low with SCL high
                                 
  ' Assume SCL is high           
  dira[SDA]~~
  outa[SDA]~                    ' Drive SDA low
  outa[SCL]~                    ' Drive SCL low

  
PRI StopI2C                            ' SDA goes LOW to HIGH with SCL High

'' From Basic_I2C_Driver
   outa[SCL]~~                         ' Drive SCL HIGH
   outa[SDA]~~                         '  then SDA HIGH
   dira[SDA]~                          ' If pullups present, they'll stay HIGH


PRI WriteI2C(data) : ackbit

'' From Basic_I2C_Driver
'' Write i2c data.  Data byte is output MSB first, SDA data line is valid
'' only while the SCL line is HIGH.  Data is always 8 bits (+ ACK/NAK).
'' SDA is assumed LOW and SCL and SDA are both left in the LOW state.

   data <<= 24
   repeat 8                            ' Output data to SDA
      outa[SDA] := (data <-= 1) & 1
      outa[SCL]~~                      ' Toggle SCL from LOW to HIGH to LOW
      outa[SCL]~
   dira[SDA]~                          ' Set SDA to input for ACK/NAK
   outa[SCL]~~
   ackbit := ina[SDA]                  ' Sample SDA when SCL is HIGH
   outa[SCL]~
   outa[SDA]~                          ' Leave SDA driven LOW
   dira[SDA]~~


PRI ReadI2C(ackbit): data

'' From Basic_I2C_Driver
'' Read in i2c data, Data byte is output MSB first, SDA data line is
'' valid only while the SCL line is HIGH.  SCL and SDA left in LOW state.
   data~
   dira[SDA]~                          ' Make SDA an input
   repeat 8                            ' Receive data from SDA
      outa[SCL]~~                      ' Sample SDA when SCL is HIGH
      data := (data << 1) | ina[SDA]
      outa[SCL]~
   outa[SDA] := ackbit                 ' Output ACK/NAK to SDA
   dira[SDA]~~
   outa[SCL]~~                         ' Toggle SCL from LOW to HIGH to LOW
   outa[SCL]~
   outa[SDA]~                          ' Leave SDA driven LOW